/*
 * 
 * AS DECLARACOES ABAIXO SAO USADAS EM TODO ARQUIVO MEMORIA.C
 * 
 */
#include "Constantes_Globals.h"

mem ram[TAM_RAM];
int posicaoLivreNucleo = 1;
int posicaoLivreUsuario = 257;
int posicaoLivreVirtual = 0;
int pagina = 1;
FILE *fp;
FILE *fp2;
FILE *fp3;
int fifoRam = 0;
int fifoVirtual = 0;



/*
 * 
 * AS FUNCOES ABAIXO SAO FUNCOES DE AUXILIO NA MANUTENCAO DO GERENCIAMENTO DA
 * MEMORIA FISICA E DA MEMORIA VIRTUAL
 * 
 */

void modificaProcesso(Processo processo, int instrucao, int proLe, int proEs, int proEx){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Funcao modifica processo \n");
    fprintf(fp3,"Processo: %d \n", processo.pid);
    fprintf(fp3,"Instrucao: %d \n", instrucao);
    fprintf(fp3,"proLe: %d* \n", proLe);
    fprintf(fp3,"proEs: %d* \n", proEs);
    fprintf(fp3,"proEx: %d* \n", proEx);
    fprintf(fp3,"******************* \n");
    if(proLe == 1)
        modificaProLe(processo, instrucao);
    if(proEs == 1)
        modificaProEs(processo, instrucao);
    if(proEx == 1)
        modificaProEx(processo, instrucao);
}

void modificaProLe(Processo processo, int instrucao){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Funcao modificaProLe \n");
    fprintf(fp3,"Processo: %d \n", processo.pid);
    fprintf(fp3,"Instrucao: %d \n", instrucao);
    fprintf(fp3,"******************* \n");
    int index = buscaInformacaoReal(processo.pid, instrucao);
    if(ram[index].protecaoLeitura == 1)
        ram[index].protecaoLeitura = 0;
    else
        ram[index].protecaoLeitura = 1;
}

void modificaProEs(Processo processo, int instrucao){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Funcao modificaProEs \n");
    fprintf(fp3,"Processo: %d \n", processo.pid);
    fprintf(fp3,"Instrucao: %d \n", instrucao);
    fprintf(fp3,"******************* \n");
    int index = buscaInformacaoReal(processo.pid, instrucao);
    if(ram[index].protecaoEscrita == 1)
        ram[index].protecaoEscrita = 0;
    else
        ram[index].protecaoEscrita = 1;
}

void modificaProEx(Processo processo, int instrucao){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Funcao modificaProEx \n");
    fprintf(fp3,"Processo: %d \n", processo.pid);
    fprintf(fp3,"Instrucao: %d \n", instrucao);
    fprintf(fp3,"******************* \n");
    int index = buscaInformacaoReal(processo.pid, instrucao);
    if(ram[index].protecaoExecucao == 1)
        ram[index].protecaoExecucao = 0;
    else
        ram[index].protecaoExecucao = 1;
}



int buscaInformacaoReal(int id, int instrucao){
        fprintf(fp3,"******************* \n");
        fprintf(fp3,"Funcao buscaInformacaoReal \n");
        fprintf(fp3,"Processo: %d \n", id);
        fprintf(fp3,"Instrucao: %d \n", instrucao);
        fprintf(fp3,"******************* \n");
    int i;
    while(i != TAM_RAM){
        if(ram[i].ms.id == id && ram[i].ms.instrucao == instrucao)
            return i;
    i++;
    }
}

void tabelaPagina(memVir mem2){
    fp2 = fopen("C:/tabelaPaginaVirtual.txt","a"); 

    fprintf(fp2,"PAGINA instrucao: %d \n",mem2.id);
    fprintf(fp2,"PAGINA id: %d \n",mem2.instrucao);
    fprintf(fp2,"PAGINA indice: %d \n",mem2.indice);
    fprintf(fp2,"PAGINA pagina: %d \n",mem2.pagina);
    fprintf(fp2,"PAGINA presenteAusente: %d \n",mem2.presenteAusente);
    fprintf(fp2,"PAGINA protecaoLeitura: %d \n",mem2.protecaoLeitura);
    fprintf(fp2,"PAGINA protecaoEscrita: %d \n",mem2.protecaoEscrita);
    fprintf(fp2,"PAGINA protecaoExecucao: %d \n",mem2.protecaoExecucao);
    fprintf(fp2,"PAGINA modificada: %d \n",mem2.modificada);
    fprintf(fp2,"PAGINA referenciada: %d \n",mem2.referenciada);
    fprintf(fp2,"PAGINA desabilitaCache: %d \n",mem2.desabilitaCache);
    fprintf(fp2,"\n\n");
}

memVir* 
converteRealVirtual(mem m){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"converteRealVirtual \n");
    memVir *memV = malloc (sizeof(memVir));
    memV->desabilitaCache = 0;
    memV->id = m.tp.id;
    memV->indice = m.ms.indice;
    memV->instrucao = m.ms.instrucao;
    memV->modificada = 1;
    memV->pagina = pagina;
    pagina++;
    memV->presenteAusente = 1;
    memV->protecaoEscrita = 0;
    memV->protecaoExecucao = 0;
    memV->protecaoLeitura = 0;
    memV->referenciada = 1;
    return memV;
    fprintf(fp3,"ID: %d \n", m.tp.id);
    fprintf(fp3,"Indice: %d \n", m.ms.indice);
    fprintf(fp3,"Pagina: %d \n", pagina);
    fprintf(fp3,"Instrucao: %d \n", m.ms.instrucao);
    fprintf(fp3,"******************* \n");
}




/* ira limpar a posicao escolhida, inserindo dados do novo processo*/
void substituiInformacaoVirtual(Processo p, int instrucao){
   // printf("Substituindo o processo %d, pelo processo %d \n",virtual[pos].id, p.pid);
  //  printf("Substituindo a instrucao %d, pela instrucao %d \n",virtual[pos].instrucao, instrucao);
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Comecou substituiInformacaoVirtual  \n");
    memVir memV;
    memV.desabilitaCache = 0;
    memV.id = p.pid;
    memV.indice = posicaoLivreVirtual;
    posicaoLivreVirtual++;
    memV.instrucao = instrucao;
    memV.modificada = 1;
    memV.pagina = pagina;
    pagina++;
    memV.presenteAusente = 1;
    memV.protecaoEscrita = 0;
    memV.protecaoExecucao = 0;
    memV.protecaoLeitura = 0;
    memV.referenciada = 1;
    salvar_memVir(&memV,1);
    fprintf(fp3,"Processo: %d \n", memV.id);
    fprintf(fp3,"Indice: %d \n", memV.indice);
    fprintf(fp3,"Instrucao: %d \n", memV.instrucao);
    fprintf(fp3,"Pagina: %d \n", memV.pagina);
    fprintf(fp3,"******************* \n");
}

/* ira limpar a posicao escolhida, inserindo dados do novo processo*/
void substituiInformacaoReal(int pos, Processo p, int instrucao){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Comecou substituiInformacaoReal  \n");
    fprintf(fp3,"Processo: %d \n", p.pid);
    fprintf(fp3,"Instrucao: %d \n", instrucao);
    fprintf(fp3,"Posicao: %d \n", pos);
    fprintf(fp3,"Substituindo o processo %d, pelo processo %d \n", ram[pos].ms.id, p.pid);
    fprintf(fp3,"Substituindo a instrucao %d, pela instrucao %d \n",ram[pos].ms.instrucao, instrucao);
    mem mem;
    ms auxI;
    auxI.indice = pos;
    auxI.pagina = pagina;
    pagina++;
    auxI.id = p.pid;
    auxI.instrucao = instrucao;
    mem.ms = auxI;
    mem.presenteAusente = 1;
    mem.modificada = 1;
    mem.referenciada = 1;
    ram[pos] = mem;
    fprintf(fp3,"******************* \n");
}


void definePaginacao(Processo processo){
    fprintf(fp3,"******************* \n");
    fprintf(fp3, "Definindo paginacao para processo %d \n", processo.pid);
    fprintf(fp3,"******************* \n");
    srand(time(NULL));
    int sort = rand() % 3;
    if(sort == 1)
        pagNRU(processo);
    else if(sort == 2)
        secondChance(processo);
    else
        fifo(processo);
}

/* Funcao verifica se id e instrucao, estao no vetor de memoria ram, se sim, retorna true, se nao, retornal false*/
bool verificaInformacaoReal(int id, int instrucao){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Iniciando verificaInformacaoReal \n");
    fprintf(fp3, "Processo: %d \n", id);
    fprintf(fp3, "Instrucao: %d \n", id);
    int i = 0;
    while(i != TAM_RAM){
        if(ram[i].ms.id == id && ram[i].ms.instrucao == instrucao)
            return true;
		fprintf(fp3,"Esta na memoria \n");
        i++;	
    }
	fprintf(fp3,"Nao esta na memoria \n");
    return false;
	 fprintf(fp3,"******************* \n");
}




/* Funcao pega menor a posicao de menor ordem do usuario (baseado em defOrdem)*/
int menorOrdemUsuarioP(){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Iniciando menorOrdemUsuarioP \n");
    int lvl = 3;	
    int i;
    for(i = INICIO_U; i < FIM_U; i++){
        if(defOrdem(i) < lvl)
            lvl = defOrdem(i);
        if(lvl == 0)
            break;
    }
    return i;
    fprintf(fp3,"Menor ordem e: %d \n", i);
    fprintf(fp3,"******************* \n");
}
/* Pega posicao com a menor posicao de ordem do nucleo (baseado em defOrdem)*/
int menorOrdemNucleoP(){
   fprintf(fp3,"******************* \n");
   fprintf(fp3,"Iniciando menorOrdemNucleoP \n");
    int lvl = 3;	
    int i;
    for(i = INICIO_N; i < FIM_N; i++){
        if(defOrdem(i) < lvl)
            lvl = defOrdem(i);
        if(lvl == 0)
            break;
    }
    return i;
    fprintf(fp3,"Menor ordem e: %d \n", i);
    fprintf(fp3,"******************* \n");
}


/* Utiliza cada posicao no vetor para verificar a ordem dela*/
int defOrdem(int pos) { 
    if (ram[pos].modificada == 1 && ram[pos].referenciada == 1)
        return 3;
    if (ram[pos].modificada == 0 && ram[pos].referenciada == 1)
        return 2;
    if (ram[pos].modificada == 1 && ram[pos].referenciada == 0)
        return 1;
    if (ram[pos].modificada == 0 && ram[pos].referenciada == 0)
        return 0;
}




/* verifica o primeiro processo que entrou no nucleo*/
int verificarPrimeiroNucleo(){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Iniciou verificarPrimeiroNucleo \n");
    int i;
    int aux = 0;
    for(i = INICIO_N ; i < FIM_N ; i++){
        if(ram[i].fifoRam <= aux){
            aux = ram[i].fifoRam; 
        }
    }
    return aux;
    fprintf(fp3,"Primeiro Nucleo: %d \n", aux);
    fprintf(fp3,"******************* \n");
}

/* verifica o primeiro processo que entrou no espaco do usuario*/
int verificarPrimeiroUsuario(){
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Iniciou verificarPrimeiroUsuario \n");
    int i;
    int aux = 0;
    for(i = INICIO_U ; i < FIM_U ; i++){
        if(ram[i].fifoRam <= aux){
            aux = ram[i].fifoRam; 
        }
    }
    return aux;
    fprintf(fp3,"Primeiro Usuario: %d \n", aux);
    fprintf(fp3,"******************* \n");
}


/*
 * 
 * OS CODIGOS ABAIXO SAO REFERENTES AO USO DIRETO DO GERENCIAMENTO DA MEMORIA
 * FISICA E DA MEMORIA VIRTUAL
 * 
 */



void insereNucleo(mem mem1, Processo processo)
{
    if(processo.tam_instrucoes_importantes > 256 - posicaoLivreNucleo ){
        printf("CHAMA PAGINACAO");
        definePaginacao(processo);
    }
    else{
         fprintf(fp3,"******************* \n");
        fprintf(fp3,"Informacoes do processo  %d \n",processo.pid);
        mem1.modificada = 0;
        mem1.referenciada = 0;
        mem1.fifoRam = fifoRam;
        fifoRam++;
        ram[posicaoLivreNucleo] = mem1;
        fprintf(fp3,"  na posicao da memoria fisica:  %d  (Valor armazenado:  %d  )\n",posicaoLivreNucleo,mem1.ms.instrucao);
        posicaoLivreNucleo++;
        fprintf(fp3,"******************* \n");
    }    
}
    
void insereUsuario(mem mem1, Processo processo)
{
    if(processo.tam_instrucoes_importantes > 1024 - posicaoLivreUsuario){
        printf("CHAMA PAGINACAO");
        definePaginacao(processo);
    }
    else{
        fprintf(fp3,"******************* \n");
        fprintf(fp3,"O processo %d \n",processo.pid);
        mem1.modificada = 0;
        mem1.referenciada = 0;
        mem1.fifoRam = fifoRam;
        fifoRam++;
        ram[posicaoLivreUsuario] = mem1;
        fprintf(fp3," entrou na posicao de memoria equivalente a %d \n",posicaoLivreUsuario);
        posicaoLivreUsuario+=1;
        fprintf(fp3," e a proxima posicao livre e %d .\n",posicaoLivreUsuario);
    }    
}


void insereMemoriaVirtual(Processo processo, int instrucao){
    substituiInformacaoVirtual(processo, instrucao);
}


void auxiliarVIPNucleo(Processo processo, mem mem1, memVir mem2){
    int i;
    for(i = 0; i < processo.tam_instrucoes_importantes ; i++){  
        fprintf(fp3,"******************* \n");
        fprintf(fp3, "Comecou auxiliarVIPNucleo \n");
        ms auxI;
        auxI.indice = posicaoLivreNucleo;
        auxI.pagina = pagina;
        pagina++;
        auxI.id = processo.pid;
        auxI.instrucao = processo.instrucoes_importantes[i];
        mem1.ms = auxI;
        fprintf(fp3,"Indice: %d \n", auxI.indice);
        fprintf(fp3,"Pagina: %d \n", auxI.pagina);
        fprintf(fp3,"ID: %d \n", auxI.id);
        fprintf(fp3,"Instrucao: %d \n", processo.instrucoes_importantes[i]);
        fprintf(fp3,"******************* \n");
        insereNucleo(mem1, processo);
    }

    fprintf(fp3,"Processo esta tendo suas instrucoes nao importantes gravadas na memoria virtual. Tipo do processo Nucleo. ID do Processo: %d. \nNumero de instrucoes do processo acima: %d\n",processo.pid,processo.tam_instrucoes_menos_importantes);
    fprintf(fp3,"******************* \n");
    for(i = 0; i < processo.tam_instrucoes_menos_importantes ; i++)
    {
        fprintf(fp3,"\nO MMU esta gravando a instrucao  %d  do processo na memoria virtual. Numero de instrucoes: %d\n",processo.instrucoes_menos_importantes[i],processo.tam_instrucoes_menos_importantes);
        fprintf(fp3,"******************* \n");
        insereMemoriaVirtual(processo,processo.instrucoes_menos_importantes[i]);
    }
}

void auxiliarVIPVirtual(Processo processo, mem mem1, memVir mem2){
    int i;
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Iniciar auxiliarVIPVirtual  \n");
    for(i = 0; i < processo.tam_instrucoes_importantes ; i++){
        ms auxI;
        auxI.indice = posicaoLivreUsuario;
        auxI.pagina = pagina;
        pagina++;
        auxI.id = processo.pid;
        auxI.instrucao = processo.instrucoes_importantes[i];
        mem1.ms = auxI;
        fprintf(fp3,"\nGravando a instrucao  %d  do processo na memoria fisica.",auxI.instrucao);
        fprintf(fp3,"******************* \n");
        insereUsuario(mem1, processo);
    }

    fprintf(fp3,"Processo esta tendo suas instrucoes nao importantes gravadas na memoria virtual. Tipo do processo Usuario. ID do Processo: %d\n",processo.pid);
    fprintf(fp3,"******************* \n");
    for(i = 0; i < processo.tam_instrucoes_menos_importantes ; i++)
    {
        fprintf(fp3,"\nGravando a instrucao  %d  do processo na memoria virtual. Numero de instrucoes: %d\n",processo.instrucoes_menos_importantes[i],processo.tam_instrucoes_menos_importantes);
        fprintf(fp3,"******************* \n");
        insereMemoriaVirtual(processo, processo.instrucoes_menos_importantes[i]);
    }
}




/*
 * 
 * ALGORITMOS DE PAGINACAO: NRU, FIFO e SEGUNDA CHANCE;
 * 
 */

/* Paginacao NRU utiliza a posicao que tem a menor ordem (vide defOrdem) para substituir*/
 void pagNRU(Processo processo){
    int i;
    int posicaoNucleo;
    int posicaoUsuario;
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Iniciando PaginacaoNRU \n");
    fprintf(fp3,"Trabalhando com processo: %d \n",processo.pid);
    if(processo.prioridade == 0){
        for(i = 0 ; i < processo.tam_instrucoes_importantes ; i++){
            fprintf(fp3,"Processo com prioridade de nucleo \n");
            posicaoNucleo = menorOrdemNucleoP();
            fprintf(fp3,"Usando posicao: %d \n", posicaoNucleo);
            salvar_memVir(converteRealVirtual(ram[i]), 1);
            fprintf(fp3,"Salvando a instrucao: %d \n", processo.instrucoes_importantes[i]);
            substituiInformacaoReal(posicaoNucleo, processo, processo.instrucoes_importantes[i]);
        }
    }
    if(processo.prioridade == 1){
        for(i = 0 ; i < processo.tam_instrucoes_importantes ; i++){
            fprintf(fp3,"Processo com prioridade de usuario \n");
            posicaoUsuario = menorOrdemUsuarioP();
            fprintf(fp3,"Usando posicao: %d \n", posicaoUsuario);
            salvar_memVir(converteRealVirtual(ram[i]), 1);
            fprintf(fp3,"Salvando a instrucao: %d \n", processo.instrucoes_importantes[i]);
            substituiInformacaoReal(posicaoUsuario, processo, processo.instrucoes_importantes[i]);
        }
    }
    for(i = 0 ; i < processo.tam_instrucoes_menos_importantes ; i++){
        fprintf(fp3,"Salvando instrucoes menos importantes \n");
        fprintf(fp3,"Salvando a instrucao: %d \n", processo.instrucoes_menos_importantes[i]);
        substituiInformacaoVirtual(processo, processo.instrucoes_menos_importantes[i]);
    }
        fprintf(fp3,"******************* \n");
}
 


/* Fifo pega primeiro processo que entrou na memoria e passa para o disco */
void fifo(Processo processo) {
    int i;
    int posicaoNucleo;
    int posicaoUsuario;
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Iniciando a paginacao FIFO\n");
    fprintf(fp3,"Trabalhando com processo: %d \n",processo.pid);
    if(processo.prioridade == 0){
        for(i = 0 ; i < processo.tam_instrucoes_importantes ; i++){
            fprintf(fp3,"Processo com prioridade de nucleo\n");
            posicaoNucleo =  verificarPrimeiroNucleo();
            salvar_memVir(converteRealVirtual(ram[i]), 1);
            substituiInformacaoReal(posicaoNucleo, processo,processo.instrucoes_importantes[i]);
            fprintf(fp3,"Posicao nucleo %d \n",posicaoNucleo);
            fprintf(fp3,"Salvando a instrucao: %d\n",processo.instrucoes_importantes[i] );
        }
    }
    else{
        for(i = 0 ; i < processo.tam_instrucoes_importantes ; i++){
            fprintf(fp3,"Processo com prioridade de usuario\n");
            posicaoUsuario = verificarPrimeiroUsuario();
            salvar_memVir(converteRealVirtual(ram[i]), 1);
            substituiInformacaoReal(posicaoUsuario, processo,processo.instrucoes_importantes[i]);
            fprintf(fp3,"Posicao usuario %d \n",posicaoUsuario);
            fprintf(fp3,"Salvando a instrucao: %d\n",processo.instrucoes_importantes[i] );
        }
    }
    for(i = 0 ; i < processo.tam_instrucoes_menos_importantes ; i++){
         fprintf(fp3,"Salvando instrucoes menos importantes \n");
         fprintf(fp3,"Salvando a instrucao: %d \n", processo.instrucoes_menos_importantes[i]);
        substituiInformacaoVirtual(processo,processo.instrucoes_menos_importantes[i]);
    }
    fprintf(fp3,"******************* \n");
}

/*
 * SEGUNDA CHANCE
 */

// Importante para toda a funcao abaixo.
int indiceCorrente = 0;

void secondChance(Processo processo){
    
    int i= 0;
    int pp = processo.prioridade;
    int minimo, maximo;
    fprintf(fp3,"******************* \n");
    fprintf(fp3,"Iniciando paginacao secondChance \n");
    fprintf(fp3,"Trabalhando com processo: %d \n",processo.pid);
    if(pp == 0){
        minimo = INICIO_N;
        maximo = FIM_N;
    }
    else{
        minimo = INICIO_U;
        maximo = FIM_U;
    }
    
    for(i = minimo; i <= maximo; i++){
        if(indiceCorrente+1 == processo.tam_instrucoes_importantes)
            break;

        if(ram[i].referenciada == 1)
            ram[i].referenciada = 0;
        
        else{
            salvar_memVir(converteRealVirtual(ram[i]), 1);
            substituiInformacaoReal(i, processo, processo.instrucoes_importantes[indiceCorrente]);
            fprintf(fp3,"Instrucao: %d \n", processo.instrucoes_importantes[indiceCorrente]);
            fprintf(fp3,"Posicao: %d \n", i);
            indiceCorrente++;
        }
    }
    if(indiceCorrente+1 != processo.tam_instrucoes_importantes)
        return secondChance(processo);
    for(i = 0 ; i < processo.tam_instrucoes_menos_importantes ; i++){
        fprintf(fp3,"Trabalhando com instrucoes nao importantes \n");
        substituiInformacaoVirtual(processo, processo.instrucoes_menos_importantes[i]);
        fprintf(fp3,"Salvando instrucao nao importante: %d \n", processo.instrucoes_menos_importantes[i]);
    }
    indiceCorrente = 0;
    return;
}



/*
 * 
 * FUNCAO "main" DA MEMORIA.C
 * 
 */



void MMU(Processo processo)
{                   
    fp = fopen("C:/log.txt","w");
    mem mem1;
    memVir mem2;
    fp3= fopen("C:/logmemoria.txt","w");
    if(processo.prioridade == 0){        
        fprintf(fp3,"Processo esta tendo suas instrucoes importantes gravadas na memoria fisica. Tipo do processo Nucleo. ID do Processo: %d\n",processo.pid);
        if(FIM_N == posicaoLivreNucleo)
           definePaginacao(processo);
        else
            auxiliarVIPNucleo(processo,mem1,mem2);
        fclose(fp);
        return;
    }
    
    if(processo.prioridade == 1){
        fprintf(fp3,"Processo esta tendo suas instrucoes importantes gravadas na memoria fisica. Tipo do processo Usuario. ID do Processo: %d\n",processo.pid);
        if(FIM_U == posicaoLivreUsuario)
            definePaginacao(processo);
        else
            auxiliarVIPVirtual(processo,mem1,mem2);
        fclose(fp);
        return;
    }
    else{
        fprintf(fp3,"\n\nErro! Processo nao tem prioridade. Programa foi finalizado.\n\n");
        fclose(fp);
        exit(1);
    }
}